#include "RtSession.h"
#include "RtRoot.h"
#include <QDebug>
#include <QScriptEngine>
#include <QFile>
#include <QTextStream>
#include <QTimer>
#include <QCoreApplication>
#include <QScriptValueIterator>
#include <QStringList>

#include "RtTypes.h"

#define PROC_EVENTS_INTERVAL 250

RtScriptEngine::RtScriptEngine(QObject *parent) : QObject(parent)
{
	engine_ = new QScriptEngine(this);
	engine_->setProcessEventsInterval ( PROC_EVENTS_INTERVAL );

	QScriptValue self = engine_->newQObject(
		this, QScriptEngine::QtOwnership,
		QScriptEngine::ExcludeSuperClassContents);

	QScriptValue globObj = engine_->globalObject();
	{
		QScriptValueIterator it(globObj);
		while (it.hasNext()) {
			it.next();
			self.setProperty(it.scriptName(), it.value(), it.flags());
		}
	}

	RtObject* rt = RtObject::root();

	QScriptValue rootObj = engine_->newQObject(rt,
		QScriptEngine::QtOwnership,
		QScriptEngine::ExcludeDeleteLater //| QScriptEngine::ExcludeSuperClassContents
		);

	self.setProperty("rt", rootObj, QScriptValue::Undeletable);

	engine_->setGlobalObject(self);

	registerRtObjectStar(engine_);
	registerVectorTypes(engine_);

	engine_->collectGarbage();
}
bool RtScriptEngine::evaluate(const QString& program, QString& ret)
{
	QScriptValue result = engine_->evaluate(program);
	ret = result.toString();
	if (engine_->hasUncaughtException()) {
		QStringList backtrace = engine_->uncaughtExceptionBacktrace();
		ret += '\n';
		ret += backtrace.join("\n");
		return false;
	}
	return (result.isError()) ? false : true;
}

bool RtScriptEngine::evaluate(const QScriptProgram& program, QString& ret)
{
	QScriptValue result = engine_->evaluate(program);
	ret = result.toString();
	if (engine_->hasUncaughtException())
	{
		QStringList backtrace = engine_->uncaughtExceptionBacktrace();
		ret += '\n';
		ret += backtrace.join("\n");
		return false;
	}
	return (result.isError()) ? false : true;
}
bool RtScriptEngine::canEvaluate(const QString& program)
{
	return engine_->canEvaluate(program);
}
bool RtScriptEngine::isEvaluating() const
{
	return engine_->isEvaluating();
}
void RtScriptEngine::abortEvaluation()
{
	engine_->abortEvaluation();
}
///////////////////// RtSession ///////////////////////////////
RtSession::RtSession(QObject *parent) : RtScriptEngine(parent)
{	
	wait_timer_ = new QTimer(this);
	wait_timer_->setSingleShot(true);
}

RtSession::~RtSession( void)
{
	Q_ASSERT(!isEvaluating());
}

void RtSession::evaluate(const QString& program)
{
	QString result;
	if ( RtScriptEngine::evaluate(program, result) )
		emit stdOut(result);
	else
		emit stdErr(result);
}
void RtSession::wait(uint ms)
{
    wait_timer_->start(ms);
    wait_aborted_ = false;
    do
        QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents, PROC_EVENTS_INTERVAL);
    while (wait_timer_->isActive());
    if (wait_aborted_)
        engine_->currentContext()->throwError(QScriptContext::UnknownError,"Wait Aborted.");
}
void RtSession::abortEvaluation()
{
	wait_timer_->stop();
	wait_aborted_ = true;
    engine_->abortEvaluation();
}
void RtSession::exec(const QString &fname)
{
    QFile file(fname);
    if (file.open(QIODevice::Text | QIODevice::ReadOnly))
    {
        QTextStream qin(&file);
        QString program = qin.readAll();
        engine_->evaluate(program,fname);
    }
    else engine_->currentContext()->throwError(QScriptContext::ReferenceError,"File not found.");
}
void RtSession::print(const QString& str)
{
    stdOut(str);
    if (!str.endsWith('\n')) stdOut(QString('\n'));
}
void RtSession::textSave(const QString &str, const QString &fname)
{
    QFile file(fname);
    if (file.open(QFile::WriteOnly | QFile::Truncate))
    {
        QTextStream qout(&file);
        qout << str;
    }
    else engine_->currentContext()->throwError("File could not be opened.");
}

QString RtSession::textLoad(const QString &fname)
{
    QString str;
    QFile file(fname);
    if (file.open(QFile::ReadOnly))
    {
        QTextStream qin(&file);
        str = qin.readAll();

    }
    else engine_->currentContext()->throwError("File could not be opened.");
    return str;
}

void RtSession::quit()
{
    emit endSession();
   // QWidget* w = qobject_cast<QWidget*>(parent());
    //if (w)
        //QCoreApplication::postEvent(w->window(), new QCloseEvent());
}
